package com.opNaga.bpmn.palettes;

import y.base.Node;
import y.option.RealizerCellRenderer;
import y.view.DropSupport;
import y.view.EdgeRealizer;
import y.view.Graph2DView;
import y.view.NodeRealizer;

import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.util.Collection;

public class DragAndDropSupport {
    protected JList realizerList;
    protected DropSupport dropSupport;


    public DragAndDropSupport(Collection realizerList, final Graph2DView view) {
        this(realizerList.toArray(), view);
    }

    public DragAndDropSupport(Object[] realizers, final Graph2DView view) {
        this(realizers, view, 120, 45);
    }

    public DragAndDropSupport(Object[] realizers, final Graph2DView view, int itemWidth, int itemHeight) {
        initializeDropSupport(view);
        initializeRealizerList(realizers, view, itemWidth, itemHeight);
        initializeDragSource();
    }

    /**
     * Creates the drop support class that can be used for dropping realizers onto the Graph2DView.
     */
    protected void initializeDropSupport(final Graph2DView view) {
        dropSupport = new DropSupport(view) {
            protected Node createNode(Graph2DView view, NodeRealizer r, double worldCoordX, double worldCoordY) {
                final Node node = super.createNode(view, r, worldCoordX, worldCoordY);
                nodeCreated(node, worldCoordX, worldCoordY);
                return node;
            }
        };

        dropSupport.setPreviewEnabled(true);
        dropSupport.setIndicatingSourceNode(true);
    }

    /**
     * Creates a nice GUI for displaying NodeRealizers.
     */
    protected void initializeRealizerList(Object[] realizers, final Graph2DView view, int itemWidth, int itemHeight) {
        realizerList = new JList(realizers);
        realizerList.setCellRenderer(createCellRenderer(itemWidth, itemHeight));

        // set the currently selected NodeRealizer as default nodeRealizer
        realizerList.addListSelectionListener(new ListSelectionListener() {
            public void valueChanged(ListSelectionEvent e) {
                if (realizerList.getSelectedValue() instanceof NodeRealizer) {
                    nodeRealizerSelected(view, (NodeRealizer) realizerList.getSelectedValue());
                } else if (realizerList.getSelectedValue() instanceof EdgeRealizer) {
                    edgeRealizerSelected(view, (EdgeRealizer) realizerList.getSelectedValue());
                }
            }
        });

        realizerList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        realizerList.setSelectedIndex(0);
    }

    /**
     * Defines the realizer list to be the drag source use the string-valued name of the realizer as transferable.
     */
    protected void initializeDragSource() {
        final DragSource dragSource = new DragSource();
        dragSource.createDefaultDragGestureRecognizer(realizerList, DnDConstants.ACTION_MOVE,
                new DragGestureListener() {
                    public void dragGestureRecognized(DragGestureEvent event) {
                        final Object value = realizerList.getSelectedValue();
                        if (value instanceof NodeRealizer) {
                            NodeRealizer nr = (NodeRealizer) value;
                            // use the drop support class to initialize the drag and drop operation.
                            dropSupport.startDrag(dragSource, nr, event, DragSource.DefaultMoveDrop);
                        } else if (value instanceof EdgeRealizer) {
                            EdgeRealizer nr = (EdgeRealizer) value;
                            // use the drop support class to initialize the drag and drop operation.
                            dropSupport.startDrag(dragSource, nr, event, DragSource.DefaultMoveDrop);
                        }
                    }
                });
    }

    /**
     * Creates the realizer cell renderer used by this class.
     */
    protected RealizerCellRenderer createCellRenderer(int itemWidth, int itemHeight) {
        return new RealizerCellRenderer(itemWidth, itemHeight);
    }

    protected void nodeCreated(Node node, double worldCoordX, double worldCoordY) {
    }

    /**
     * Callback method that is triggered whenever the selection changes in the JList.
     * This method sets the given NodeRealizer as the view's graph default node realizer.
     */
    protected void nodeRealizerSelected(Graph2DView view, NodeRealizer realizer) {
        view.getGraph2D().setDefaultNodeRealizer(realizer);
    }

    /**
     * Callback method that is triggered whenever the selection changes in the JList.
     * This method sets the given EdgeRealizer as the view's graph default node realizer.
     */
    protected void edgeRealizerSelected(Graph2DView view, EdgeRealizer realizer) {
        view.getGraph2D().setDefaultEdgeRealizer(realizer);
    }

    /**
     * Return the JList that has been configured by this support class.
     */
    public JList getList() {
        return realizerList;
    }
}